package com.example.lottery.service.impl;

import com.example.lottery.common.errorcode.ControllerErrorCodeConstants;
import com.example.lottery.common.errorcode.ServiceErrorCodeConstants;
import com.example.lottery.common.exception.ControllerException;
import com.example.lottery.common.exception.ServiceException;
import com.example.lottery.common.utils.JacksonUtil;
import com.example.lottery.common.utils.RedisUtil;
import com.example.lottery.controller.param.ActivityCreateParam;
import com.example.lottery.controller.param.FindActivityListParam;
import com.example.lottery.controller.param.GetActivityDetailParam;
import com.example.lottery.controller.result.ActivityCreateResult;
import com.example.lottery.dao.dataobject.*;
import com.example.lottery.dao.mapper.*;
import com.example.lottery.service.IActivityService;
import com.example.lottery.service.dto.*;
import com.example.lottery.service.enums.ActivityEnum;
import com.example.lottery.service.enums.ActivityPrizeEnum;
import com.example.lottery.service.enums.ActivityUserEnum;
import com.example.lottery.service.enums.PrizeTiersEnum;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Slf4j
@Service
public class ActivityServiceImpl implements IActivityService {
    @Autowired
    private ActivityMapper activityMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private PrizeMapper prizeMapper;
    @Autowired
    private ActivityUserMapper activityUserMapper;
    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;
    @Autowired
    private RedisUtil redisUtil;

    private static final String ACTIVITY_PREFIX="ACTIVITY_";
    private static final Long TIME=60*60*24*3l;//3天
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ActivityCreateDTO create(ActivityCreateParam activityCreateParam) {
        //1-    参数校验
        checkActivityCreateParam(activityCreateParam);


        //2-    创建活动
        ActivityDO activityDO=new ActivityDO();
        activityDO.setActivityName(activityCreateParam.getActivityName());
        activityDO.setDescription(activityCreateParam.getDescription());
        activityDO.setStatus(ActivityEnum.RUNNING.name());

        activityMapper.insertSelective(activityDO);
        long id=activityDO.getId();

        //3-    将圈选的用户信息存入活动用户表
        List<ActivityCreateParam.ActivityUserInfo> activityUserList = activityCreateParam.getActivityUserList();
        List<ActivityUserDO> activityUserDOS = activityUserList.stream().map(activityUserInfo -> {
            ActivityUserDO activityUserDO = new ActivityUserDO();
            activityUserDO.setActivityId(id);
            activityUserDO.setUserId(activityUserInfo.getUserId());
            activityUserDO.setUserName(activityUserInfo.getUserName());
            activityUserDO.setStatus(ActivityUserEnum.INIT.name());
            return activityUserDO;
        }).collect(Collectors.toList());
        activityUserMapper.insertList(activityUserDOS);


        //4-    将圈选的奖品信息存入活动奖品表
        List<ActivityCreateParam.ActivityPrizeInfo> activityPrizeList = activityCreateParam.getActivityPrizeList();
        List<ActivityPrizeDO> activityPrizeDOS = activityPrizeList.stream().map(activityPrizeInfo -> {
            ActivityPrizeDO activityPrizeDO = new ActivityPrizeDO();
            activityPrizeDO.setActivityId(id);
            activityPrizeDO.setPrizeId(activityPrizeInfo.getPrizeId());
            activityPrizeDO.setPrizeAmount(activityPrizeInfo.getPrizeAmount());
            activityPrizeDO.setPrizeTiers(activityPrizeInfo.getPrizeTiers());
            activityPrizeDO.setStatus(ActivityPrizeEnum.INIT.name());
            return activityPrizeDO;
        }).collect(Collectors.toList());
        activityPrizeMapper.insertList(activityPrizeDOS);

        //5-    整合活动信息
        // key:activityId 根据活动Id获取 value:整合的信息

        // 获取奖品详情信息
        List<Long> prizeIds = activityPrizeDOS.stream().map(
                activityPrizeDO -> {
                    return activityPrizeDO.getPrizeId();
                }).distinct().collect(Collectors.toList());

        List<PrizeDO> prizeDOList=prizeMapper.selectListByIds(prizeIds);

        //开始整合信息
        ActivityDetailDTO activityDetailDTO=convertToActivityDetailDTO(activityDO, activityUserDOS, activityPrizeDOS, prizeDOList);

        //6-    存入redis
        cacheActivity(activityDetailDTO);

        //7-    构造返回值
        return new ActivityCreateDTO(id);
    }

    @Override
    public FindActivityDTO<ActivityDTO> findActivity(FindActivityListParam findActivityParam) {
        //1-    校验参数是否合法
        if(findActivityParam.getPageSize()<=0||findActivityParam.getCurrentPage()<=0){
            log.warn(ServiceErrorCodeConstants.ACTIVITY_PARAM_PAGE_ERROR.getMsg());
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PARAM_PAGE_ERROR);
        }
        //2-    获取活动列表
        //SELECT * FROM activity ORDER BY id desc limit 0,2
        //limit 假设PageSize=10 那么 第一页 limit 0,10  第二页 10,10 第三页 20,10
        //第一页时，currentPage=1，但是param1需要0，所以param1=(currentPage-1)*pageSize
        List<ActivityDO> activityDOS = activityMapper.selectActivityListByPage(findActivityParam.getParam(),
                findActivityParam.getPageSize());

        //3-    获取活动总条数
        int count= activityMapper.selectAllActivityCount();
        //4-    构造返回值
        List<ActivityDTO> activityDTOS = activityDOS.stream().map(activityDO -> {
            ActivityDTO activityDTO = new ActivityDTO();
            activityDTO.setId(activityDO.getId());
            activityDTO.setGmtCreate(activityDO.getGmtCreate());
            activityDTO.setGmtModified(activityDO.getGmtModified());
            activityDTO.setActivityName(activityDO.getActivityName());
            activityDTO.setDescription(activityDO.getDescription());
            activityDTO.setStatus(ActivityEnum.forName(activityDO.getStatus()));
            return activityDTO;
        }).collect(Collectors.toList());
        return new FindActivityDTO<ActivityDTO>(count,activityDTOS);
    }

    @Override
    public ActivityDetailDTO findActivityDetail(GetActivityDetailParam getActivityDetailParam) {
        //1-    参数校验
        Long activityId=getActivityDetailParam.getActivityId();
        if(activityId<=0){
            log.warn(ServiceErrorCodeConstants.ACTIVITY_DETAIL_PARAM_ERROR.getMsg());
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_DETAIL_PARAM_ERROR);
        }
        //2-    判断redis中是否有活动Id
        if(!redisUtil.hasKey("activity:" + ACTIVITY_PREFIX + activityId)){
            //redis中没有活动Id
            //2-1   获取活动信息
            ActivityDO activityDO = activityMapper.selectByPrimaryKey(activityId);
            //2-2   获取活动圈选的奖品信息
            List<ActivityPrizeDO> activityPrizeDOS=activityPrizeMapper.selectPrizeInfoByActivityId(activityId);
            //2-3   获取活动圈选的用户信息
            List<ActivityUserDO> activityUserDOS=activityUserMapper.selectUserInfoByActivityId(activityId);
            //2-4   获取奖品详细信息
            List<Long> prizeIds = activityPrizeDOS.stream().map(
                    activityPrizeDO -> {
                        return activityPrizeDO.getPrizeId();
                    }).distinct().collect(Collectors.toList());
            List<PrizeDO> prizeDOList=prizeMapper.selectListByIds(prizeIds);
            //2-5   整合信息
            ActivityDetailDTO activityDetailDTO = convertToActivityDetailDTO(activityDO, activityUserDOS, activityPrizeDOS, prizeDOList);
            //2-6   存储在redis中
            cacheActivity(activityDetailDTO);
        }
        //3     从redis中获取ActivityDetailDTO
        ActivityDetailDTO activityDetailDTO = getActivityDetailDTO(activityId);
        //4     返回值
        return activityDetailDTO;
    }

    /**
     * 更新缓存（状态扭转）
     * @param activityId
     */
    @Override
    public void updateCache(Long activityId) {
        if(activityId==null){
            log.warn("缓存的活动id为空");
            throw new ServiceException(ServiceErrorCodeConstants.DRAW_PRIZE_CACHE_ACTIVITY_ID_NULL);
        }
        //1-    获取活动信息、圈选奖品信息、圈选人员信息、奖品详情
        ActivityDO activityDO = activityMapper.selectByPrimaryKey(activityId);
        if(activityDO==null){
            log.warn("活动不存在");
            throw new ServiceException(ServiceErrorCodeConstants.DRAW_PRIZE_ACTIVITY_NOT_EXIST);
        }
        List<ActivityPrizeDO> activityPrizeDOS = activityPrizeMapper.selectPrizeInfoByActivityId(activityId);
        List<ActivityUserDO> activityUserDOS = activityUserMapper.selectUserInfoByActivityId(activityId);
        // 获取奖品详情信息
        List<Long> prizeIds = activityPrizeDOS.stream().map(
                activityPrizeDO -> {
                    return activityPrizeDO.getPrizeId();
                }).distinct().collect(Collectors.toList());

        List<PrizeDO> prizeDOList=prizeMapper.selectListByIds(prizeIds);

        //2-    整合
        ActivityDetailDTO activityDetailDTO = convertToActivityDetailDTO(activityDO, activityUserDOS, activityPrizeDOS, prizeDOList);

        //3-    存入redis
        cacheActivity(activityDetailDTO);
        log.info("存入redis成功 activityDetailDTO:{}",JacksonUtil.writeValueAsString(getActivityDetailDTO(activityId)));
    }

    /**
     * 将对象缓存到redis中
     * @param activityDetailDTO 待缓存对象
     */
    private void cacheActivity(ActivityDetailDTO activityDetailDTO) {
        if(activityDetailDTO==null||activityDetailDTO.getActivityId()==null){
            log.warn("缓存的活动信息不存在");
            return;
        }
        //redis缓存
        if(false==redisUtil.set("activity:"+ACTIVITY_PREFIX+activityDetailDTO.getActivityId(),
                    JacksonUtil.writeValueAsString(activityDetailDTO),TIME)){
                //说明抛出异常了
                log.warn("redis缓存异常 ActivityDetailDTO:{}",activityDetailDTO);
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_REDIS_STATUS_ERROR);
        }
    }

    /**
     * 根据活动id从redis获取ActivityDetailDTO
     * @param activityId
     * @return
     */
    private ActivityDetailDTO getActivityDetailDTO(Long activityId){
        if(activityId==null){
            log.warn("活动id为空");
            return null;
        }
        //redis获取
        try{
            String str = redisUtil.get("activity:" + ACTIVITY_PREFIX + activityId);
            //json反序列化
            return JacksonUtil.readValue(str, ActivityDetailDTO.class);
        }catch (Exception e){
            log.warn("redis获取值异常 activityId:{}",activityId,e);
            return null;
        }
    }


    /**
     * 整合信息
     * @param activityDO 活动信息
     * @param activityUserDOS 圈选人员信息
     * @param activityPrizeDOS 圈选奖品信息
     * @param prizeDOList 奖品的详情信息
     * @return
     */
    private ActivityDetailDTO convertToActivityDetailDTO(ActivityDO activityDO,
                                                         List<ActivityUserDO> activityUserDOS,
                                                         List<ActivityPrizeDO> activityPrizeDOS,
                                                         List<PrizeDO> prizeDOList) {
        //一个活动id对应一个活动详情信息
        ActivityDetailDTO activityDetailDTO=new ActivityDetailDTO();
        activityDetailDTO.setActivityId(activityDO.getId());
        activityDetailDTO.setActivityName(activityDO.getActivityName());
        activityDetailDTO.setDescription(activityDO.getDescription());
        activityDetailDTO.setStatus(ActivityEnum.forName(activityDO.getStatus()));


        //圈选奖品信息 转换为 List<ActivityDetailDTO.PrizeDTO>
        List<ActivityDetailDTO.PrizeDTO> prizeInfos = activityPrizeDOS.stream().map(
                activityPrizeDO -> {
            ActivityDetailDTO.PrizeDTO prizeDTO = new ActivityDetailDTO.PrizeDTO();

            //圈选奖品信息
            prizeDTO.setPrizeId(activityPrizeDO.getPrizeId());
            prizeDTO.setPrizeAmount(activityPrizeDO.getPrizeAmount());
            prizeDTO.setPrizeTiers(PrizeTiersEnum.forName(activityPrizeDO.getPrizeTiers()));
            prizeDTO.setStatus(ActivityPrizeEnum.forName(activityPrizeDO.getStatus()));

            //奖品详情信息  要确保prizeDOList和activityPrizeDOS的userId相同才能赋值
            // prizeDOList:[{userId:1},{userId:2}...]
            // activityPrizeDOS:[userId:2]
            //判断userId是否相同，相同的在赋值
            Optional<PrizeDO> optionalPrizeDO = prizeDOList.stream().filter(prizeDO -> {
            return prizeDO.getId().equals(activityPrizeDO.getPrizeId());
            }).findFirst();
            optionalPrizeDO.ifPresent(prizeDO -> {
                prizeDTO.setName(prizeDO.getName());
                prizeDTO.setDescription(prizeDO.getDescription());
                prizeDTO.setPrice(prizeDO.getPrice());
                prizeDTO.setImageUrl(prizeDO.getImageUrl());
            });
            return prizeDTO;
        }).collect(Collectors.toList());

        activityDetailDTO.setActivityPrizeList(prizeInfos);


        //圈选人员信息 转换为List<ActivityDetailDTO.UserDTO>
        List<ActivityDetailDTO.UserDTO> userInfos = activityUserDOS.stream().map(activityUserDO -> {
            ActivityDetailDTO.UserDTO userDTO = new ActivityDetailDTO.UserDTO();
            userDTO.setUserId(activityUserDO.getUserId());
            userDTO.setUserName(activityUserDO.getUserName());
            userDTO.setStatus(ActivityUserEnum.forName(activityUserDO.getStatus()));
            return userDTO;
        }).collect(Collectors.toList());
        activityDetailDTO.setActivityUserList(userInfos);

        return activityDetailDTO;
    }

    private void checkActivityCreateParam(ActivityCreateParam activityCreateParam) {
        //1-    判断userId是否存在
        List<ActivityCreateParam.ActivityUserInfo> activityUserList = activityCreateParam.getActivityUserList();
        List<Long> userIds=activityUserList.stream()
                .map((activityUserInfo)->{
                    return activityUserInfo.getUserId();
                })
                .distinct()
                .collect(Collectors.toList());
        List<Long> existUserIds = userMapper.selectByuserIds(userIds);
        //需要对existUserIds判空避免空指针
        if(existUserIds.isEmpty()){
            log.warn(ServiceErrorCodeConstants.ACTIVITY_PARAM_USER_ERROR.getMsg());
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PARAM_USER_ERROR);
        }
        //遍历userIds 如果不存在的就抛异常
        userIds.forEach(id->{
            if(!existUserIds.contains(id)){
                log.warn(ServiceErrorCodeConstants.ACTIVITY_PARAM_USER_ERROR.getMsg());
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PARAM_USER_ERROR);
            }
        });

        //2-    判断prize信息是否合法
        List<ActivityCreateParam.ActivityPrizeInfo> activityPrizeList = activityCreateParam.getActivityPrizeList();
        List<Long> prizeIds = activityPrizeList.stream().map(activityPrizeInfo -> {
            return activityPrizeInfo.getPrizeId();
        }).distinct().collect(Collectors.toList());
        List<Long> existPrizeIds = prizeMapper.selectByPrizeIds(prizeIds);
        //需要对existPrizeIds判空避免空指针
        if(existPrizeIds.isEmpty()){
            log.warn(ServiceErrorCodeConstants.ACTIVITY_PARAM_PRIZE_ERROR.getMsg());
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PARAM_PRIZE_ERROR);
        }
        prizeIds.forEach(id->{
            if(!existPrizeIds.contains(id)){
                log.warn(ServiceErrorCodeConstants.ACTIVITY_PARAM_PRIZE_ERROR.getMsg());
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PARAM_PRIZE_ERROR);
            }
        });

        //3-    人员数目大于奖品数目  奖品数目:奖品种类+每一类的数目之和
        long userAmount=activityUserList.size();
        long prizeAmount = activityPrizeList.stream().mapToLong(activityPrizeInfo -> {
            return activityPrizeInfo.getPrizeAmount();
        }).sum();

        if(userAmount<=prizeAmount){
            log.warn(ServiceErrorCodeConstants.ACTIVITY_PARAM_AMOUNT_ERROR.getMsg());
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PARAM_AMOUNT_ERROR);
        }

        //4-    判断奖品等级是否合法
        activityCreateParam.getActivityPrizeList().forEach(activityPrizeInfo -> {
            if(null==PrizeTiersEnum.forName(activityPrizeInfo.getPrizeTiers())){
                log.warn(ServiceErrorCodeConstants.ACTIVITY_PRIZE_TIERS_ERROR.getMsg());
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_TIERS_ERROR);
            }
        });

    }
}
